{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Script version 2.2.2 for Machine Learning Core (MLC)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## General settings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os, datetime\n",
    "current_directory = os.path.join(os.getcwd(), datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'))\n",
    "os.makedirs(current_directory)\n",
    "print(\"Current directory: \" + current_directory)\n",
    "arff_filename = os.path.join(current_directory, \"features.arff\")\n",
    "ucf_filename = os.path.join(current_directory, \"MLC_configuration.ucf\")\n",
    "\n",
    "# private import:\n",
    "import mlc_configurator\n",
    "from mlc_configurator import *\n",
    "import decision_tree_generator\n",
    "from decision_tree_generator import generateDecisionTree\n",
    "from decision_tree_generator import generate_subset_of_ARFF\n",
    "import mlc_test\n",
    "from mlc_test import *\n",
    "\n",
    "# Unico version check: \n",
    "from win32com.client import Dispatch\n",
    "ver_parser = Dispatch('Scripting.FileSystemObject')\n",
    "mlc_app = \"C:/Program Files (x86)/STMicroelectronics/Unico-GUI/unico.exe\"  ## Windows\n",
    "unico_version = \"0.0.0.0\"\n",
    "if os.path.isfile(mlc_app):\n",
    "    unico_version = ver_parser.GetFileVersion(mlc_app)\n",
    "    print(\"Unico version: \" + unico_version)\n",
    "expected_version = \"9.11.0.0\"\n",
    "if unico_version != expected_version:\n",
    "    print(\"\\nERROR: wrong Unico version. Please download version \" + expected_version + \" from: https://www.st.com/en/development-tools/unico-gui.html\")\n",
    "\n",
    "device_name = \"LSM6DSOX\"    ## list of supported devices available with mlc_configurator.get_devices()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1: Load log files, label data, define decision tree results\n",
    "In this step data are loaded and results are assigned. \n",
    "Decision tree results can be configured by associating result names to result values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Load single files\n",
    "datalog1 = \"../Logs/log1.txt\"\n",
    "result1  = \"still\"\n",
    "datalog2 = \"../Logs/log2.txt\"\n",
    "result2  = \"walk\"\n",
    "datalog3 = \"../Logs/log3.txt\"\n",
    "result3  = \"fastwalk\"\n",
    "\n",
    "# Load files from a folder\n",
    "#datalog1 = os.listdir(\"../Logs/\")\n",
    "#result1 = []\n",
    "#for x in range(0, len(datalog1)):\n",
    "    #datalog1[x] = \"../Logs/\" + datalog1[x]\n",
    "    #result1.append(\"still\")\n",
    "\n",
    "datalogs = [datalog1, datalog2, datalog3]\n",
    "results  = [result1, result2, result3]\n",
    "\n",
    "# Assign values to results for each decision tree\n",
    "result_names  = [] # leave empty here\n",
    "result_values = [] # leave empty here\n",
    "# Assign values to results for decision tree 1:\n",
    "result_names.append(  [result1, result2, result3] )\n",
    "result_values.append( [0,       4,       8] )\n",
    "# Assign values to results for decision tree 2:\n",
    "result_names.append(  [] )\n",
    "result_values.append( [] )\n",
    "# Assign values to results for decision tree 3:\n",
    "result_names.append(  [] )\n",
    "result_values.append( [] )\n",
    "# Assign values to results for decision tree 4:\n",
    "result_names.append(  [] )\n",
    "result_values.append( [] )\n",
    "# Assign values to results for decision tree 5:\n",
    "result_names.append(  [] )\n",
    "result_values.append( [] )\n",
    "# Assign values to results for decision tree 6:\n",
    "result_names.append(  [] )\n",
    "result_values.append( [] )\n",
    "# Assign values to results for decision tree 7:\n",
    "result_names.append(  [] )\n",
    "result_values.append( [] )\n",
    "# Assign values to results for decision tree 8:\n",
    "result_names.append(  [] )\n",
    "result_values.append( [] )\n",
    "\n",
    "dectree_filenames = []\n",
    "for i in range(0,8): \n",
    "    if not result_names[i]:\n",
    "        break\n",
    "    else: \n",
    "        dectree_filenames.append(os.path.join(current_directory, \"dectree{}.txt\".format(i+1)))\n",
    "n_decision_trees = i\n",
    "print('n_decision_trees = %d' % (n_decision_trees))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 2: Generate ARFF files\n",
    "ARFF files are CSV files where the first n lines declare data types of CSV columns.\n",
    "By editing lines below, we decide MLC configuration in terms of:\n",
    "* filters\n",
    "* features\n",
    "All the features are computed within a defined time window ('window_length').\n",
    "Other device settings like MLC data rate, sensor data rate and full scale are defined here.\n",
    "The module 'mlc_configurator.py' is used for this step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# device settings\n",
    "mlc_odr = \"26 Hz\"                        ## list of allowed values available with mlc_configurator.get_mlc_odr(device_name)\n",
    "input_type = \"accelerometer+gyroscope\"   ## list of allowed values available with mlc_configurator.get_mlc_input_type(device_name)\n",
    "accelerometer_fs = \"2 g\"                 ## list of allowed values available with mlc_configurator.get_accelerometer_fs(device_name)\n",
    "accelerometer_odr = \"26 Hz\"              ## list of allowed values available with mlc_configurator.get_accelerometer_odr(device_name)\n",
    "gyroscope_fs = \"2000 dps\"                ## list of allowed values available with mlc_configurator.get_gyroscope_fs(device_name)\n",
    "gyroscope_odr = \"26 Hz\"                  ## list of allowed values available with mlc_configurator.get_gyroscope_odr(device_name)            \n",
    "window_length = 52                       ## Window length (supported values: from 1 to 255)\n",
    "\n",
    "# filters configuration\n",
    "# the list of available filters can be obtained with mlc_configurator.get_filter_names(input_type)\n",
    "filters_list = []   \n",
    "#filters_list.append(mlc_configurator.mlc_filter(\"filter_1\", \"HP_Acc_XYZ\"))\n",
    "#filters_list.append(mlc_configurator.mlc_filter(\"filter_2\", \"BP_Acc_V\", coef_a2=-0.5, coef_a3=0.5, coef_gain=1))\n",
    "#filters_list.append(mlc_configurator.mlc_filter(\"filter_3\", \"IIR2_Acc_V\", coef_b1=0.59, coef_b2=1.19, coef_b3=0.59, coef_a2=-1.01, coef_a3=0.36))\n",
    "\n",
    "# features configuration\n",
    "# full list of available features can be obtained with mlc_configurator.get_feature_names()\n",
    "# full list of available inputs can be obtained with   mlc_configurator.get_mlc_inputs(device_name, input_type)\n",
    "features_list = []\n",
    "features_list.append(mlc_configurator.mlc_feature(\"MEAN\", \"Acc_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"VARIANCE\", \"Acc_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"ENERGY\", \"Acc_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"PEAK_TO_PEAK\", \"Acc_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"ZERO_CROSSING\", \"Acc_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"POSITIVE_ZERO_CROSSING\", \"Acc_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"NEGATIVE_ZERO_CROSSING\", \"Acc_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"PEAK_DETECTOR\", \"Acc_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"POSITIVE_PEAK_DETECTOR\", \"Acc_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"NEGATIVE_PEAK_DETECTOR\", \"Acc_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"MINIMUM\", \"Acc_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"MAXIMUM\", \"Acc_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"MEAN\", \"Gyr_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"VARIANCE\", \"Gyr_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"ENERGY\", \"Gyr_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"PEAK_TO_PEAK\", \"Gyr_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"ZERO_CROSSING\", \"Gyr_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"POSITIVE_ZERO_CROSSING\", \"Gyr_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"NEGATIVE_ZERO_CROSSING\", \"Gyr_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"PEAK_DETECTOR\", \"Gyr_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"POSITIVE_PEAK_DETECTOR\", \"Gyr_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"NEGATIVE_PEAK_DETECTOR\", \"Gyr_V\", 0.5))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"MINIMUM\", \"Gyr_V\"))\n",
    "features_list.append(mlc_configurator.mlc_feature(\"MAXIMUM\", \"Gyr_V\"))\n",
    "# Some examples of features on filtered inputs: \n",
    "#features_list.append(mlc_configurator.mlc_feature(\"MEAN\",\"Acc_X_filter_1\"))\n",
    "#features_list.append(mlc_configurator.mlc_feature(\"ENERGY\", \"Acc_V_filter_2\"))\n",
    "#features_list.append(mlc_configurator.mlc_feature(\"ZERO_CROSSING\", \"Acc_V_filter_3\", 0.5))\n",
    "\n",
    "mlc_configurator.arff_generator( device_name = device_name, \n",
    "               datalogs = datalogs, \n",
    "               results = results, \n",
    "               mlc_odr = mlc_odr, \n",
    "               input_type = input_type, \n",
    "               accelerometer_fs = accelerometer_fs, \n",
    "               accelerometer_odr = accelerometer_odr, \n",
    "               gyroscope_fs = gyroscope_fs, \n",
    "               gyroscope_odr = gyroscope_odr, \n",
    "               n_decision_trees = n_decision_trees, \n",
    "               window_length = window_length, \n",
    "               filters_list = filters_list,  \n",
    "               features_list = features_list, \n",
    "               arff_filename = arff_filename )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 3: Decision Tree generation\n",
    "The module 'decision_tree_generator.py' is used to generate decision trees through sklearn."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "if (n_decision_trees == 1):\n",
    "    dectree_accuracy, dectree_nodes = decision_tree_generator.generateDecisionTree(\n",
    "                                                        arff_filename = arff_filename, \n",
    "                                                        dectree_filename = dectree_filenames[0])\n",
    "else:\n",
    "    for i in range(n_decision_trees) :\n",
    "        arff_filename_i = arff_filename + str(i+1)\n",
    "        decision_tree_generator.generate_subset_of_ARFF( arff_filename = arff_filename, \n",
    "                                                         arff_subset_filename = arff_filename_i, \n",
    "                                                         classes_subset = result_names[i] )\n",
    "        print(\"\\n# Decision Tree %d:\" %(i+1))\n",
    "        dectree_accuracy, dectree_nodes = decision_tree_generator.generateDecisionTree( \n",
    "                                               arff_filename = arff_filename_i, \n",
    "                                               dectree_filename = dectree_filenames[i] )\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 4: Generation of .ucf file\n",
    "Settings and files from 'Step 2' and 'Step 3' are used in 'Step 4' to generate the MLC configuration in a .ucf file (a text file containing a sequence of register addresses and corresponding values). \n",
    "Meta classifiers, which are filters on the outputs of the decision trees, can be configured here. \n",
    "The module 'mlc_configurator.py' is used for this step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Meta-classifiers\n",
    "# metaclassifierX_values contains the end counter values of the meta classifier associated to the decision tree 'X'\n",
    "# 4 end counter values are available in LSM6DSOX (the first 4 values in \"metaclasifierX_values\")\n",
    "# 8 end counter values are available in LSM6DSRX/ISM330DHCX (the 8 values in \"metaclasifierX_values\")\n",
    "# values allowed for end counters are from 0 to 14\n",
    "metaclassifier1_values = \"0,0,0,0,0,0,0,0\"\n",
    "metaclassifier2_values = \"0,0,0,0,0,0,0,0\"\n",
    "metaclassifier3_values = \"0,0,0,0,0,0,0,0\"\n",
    "metaclassifier4_values = \"0,0,0,0,0,0,0,0\"\n",
    "metaclassifier5_values = \"0,0,0,0,0,0,0,0\"\n",
    "metaclassifier6_values = \"0,0,0,0,0,0,0,0\"\n",
    "metaclassifier7_values = \"0,0,0,0,0,0,0,0\"\n",
    "metaclassifier8_values = \"0,0,0,0,0,0,0,0\"\n",
    "metaclassifier_values = [metaclassifier1_values, metaclassifier2_values, metaclassifier3_values, metaclassifier4_values, metaclassifier5_values, metaclassifier6_values, metaclassifier7_values, metaclassifier8_values]\n",
    "\n",
    "mlc_configurator.ucf_generator( device_name = device_name, \n",
    "                                arff_filename = arff_filename, \n",
    "                                dectree_filenames = dectree_filenames,\n",
    "                                result_names = result_names, \n",
    "                                result_values = result_values, \n",
    "                                metaclassifier_values = metaclassifier_values, \n",
    "                                ucf_filename = ucf_filename )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## (Optional) Testing decision tree\n",
    "This section allows to test the .arff file on the decision tree.\n",
    "The .arff file can be generated in Step2 after selecting data patterns (in Step1) and Features (in Step2). \n",
    "The module 'mlc_test.py' is used for this step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Testing\n",
    "testresults_filename = os.path.join(current_directory, \"test_results.txt\")\n",
    "mlc_test.test_arff_on_decisiontree( dectree_filename = dectree_filenames[0], \n",
    "                                    arff_filename = arff_filename, \n",
    "                                    testresults_filename = testresults_filename )\n",
    "\n",
    "# show test results:\n",
    "file_o = open(testresults_filename)   \n",
    "content = file_o.read()                \n",
    "print(content)                      \n",
    "file_o.close()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
